package main

import (
	"bufio"
	"io"
	"os"
	"os/exec"

	"github.com/spf13/pflag"
)

type selpgArgs struct {
	startPage   int
	endPage     int
	pageLen     int  // confict with pageType!!
	pageType    bool // true->-f, false-> lNumber
	inputFile   string
	destination string
}

func main() {
	var args selpgArgs
	// 处理参数
	processArgs(&args)
	processInput(&args)
}

// 处理和检查参数
func processArgs(args *selpgArgs) {
	pflag.IntVarP(&(args.startPage), "startPagr", "s", -1, "start page")
	pflag.IntVarP(&(args.endPage), "endPage", "e", -1, "end page")
	pflag.IntVarP(&(args.pageLen), "pageLen", "l", 72, "the length of page")
	pflag.BoolVarP(&(args.pageType), "pageType", "f", false, "page type: true->delim '\\f', false->fixed lines (default)")
	pflag.StringVarP(&(args.destination), "destination", "d", "", "print to destination")

	pflag.Parse() //处理参数

	// 检查参数
	err := ""
	if args.startPage == -1 || args.endPage == -1 {
		// 强制参数必须指定
		err = "ERROR: startPage and endPage are mandatory options\n"
	} else if args.startPage < 1 || args.endPage < 1 {
		// 指定时保证都为非负数
		err = "ERROR: start/endPage shouble all be >= 1\n"
	} else if args.startPage > args.endPage {
		// 保证参数合理，即结束页大于等于开始页
		err = "ERROR: endPage should not be less than startPage\n"
	}
	// 如果err不为空串，代表有错误
	if err != "" {
		// 写入标准错误中
		os.Stderr.Write([]byte(err))
		pflag.Usage()
		os.Exit(-1)
	}

	// NArg是处理完flags后的剩余参数个数
	if pflag.NArg() > 0 {
		// 这里即使有多个参数也只需要取第一个
		args.inputFile = pflag.Arg(0)
	} else {
		args.inputFile = ""
	}
}

func processInput(args *selpgArgs) {
	// 打开文件
	var reader *bufio.Reader
	_ = reader // without this get error:reader declared but not used

	if args.inputFile == "" {
		// 如果没有指定文件名，则从标准输入中读取
		reader = bufio.NewReader(os.Stdin)
	} else {
		// 否则打开文件
		inputFile, err := os.Open(args.inputFile)
		if err != nil {
			os.Stderr.Write([]byte("ERROR: open file failed!\n"))
			os.Exit(-1)
		}
		defer inputFile.Close()
		// 将文件作为输入
		reader = bufio.NewReader(inputFile)
	}

	// 判断输出
	if args.destination == "" {
		outputToStdout(reader, args)
	} else {
		outputToDestination(reader, args)
	}
}

func outputToStdout(reader *bufio.Reader, args *selpgArgs) {
	// 创建标准输出的缓冲写
	writer := bufio.NewWriter(os.Stdout)

	lineNum := 0 // 行数
	pageNum := 1 // 页数

	delim := '\n'
	if args.pageType == true {
		delim = '\f'
	}

	for {
		// 根据delim读取输入
		str, err := reader.ReadBytes(byte(delim))
		if err != nil {
			if err == io.EOF {
				// 将这一次的读入也写出
				if _, err := writer.Write(str); err != nil {
					os.Stderr.Write([]byte("ERROR: write bytes failed!\n"))
					os.Exit(-1)
				}
				// 手动刷新，使得写缓冲写出
				writer.Flush()
				break
			} else {
				os.Stderr.Write([]byte("ERROR: readbytes from file failed!\n"))
				os.Exit(-1)
			}
		}

		// 输出到标准输出或者管道
		if pageNum >= args.startPage && pageNum <= args.endPage {
			if _, err := writer.Write(str); err != nil {
				os.Stderr.Write([]byte("ERROR: write bytes failed!\n"))
				os.Exit(-1)
			}
		}

		if args.pageType == true {
			// 如果是\f定界，此时已经是一页
			pageNum++
		} else {
			// 否则此时只是增加行
			lineNum++
			// 判断是否足够一页
			if lineNum == args.pageLen {
				lineNum = 0 // 恢复计数
				pageNum++
			}
		}

		if pageNum > args.endPage {
			writer.Flush()
			break
		}
	}
	// 检查页号
	if pageNum < args.startPage {
		os.Stderr.Write([]byte("ERROR: start page# is bigger than total page#\n"))
		os.Exit(-1)
	}

	if pageNum < args.endPage {
		os.Stderr.Write([]byte("ERROR: end page# is bigger than total page#\n"))
		os.Exit(-1)
	}
}

func outputToDestination(reader *bufio.Reader, args *selpgArgs) {
	// 创建执行打印机的命令
	cmd := exec.Command("./" + args.destination)
	// 获取其标准输入管道
	stdinPipe, err := cmd.StdinPipe()
	if err != nil {
		os.Stderr.Write([]byte(err.Error()))
		os.Exit(-1)
	}
	// 开始执行，此时不会阻塞
	if err := cmd.Start(); err != nil {
		os.Stderr.Write([]byte(err.Error()))
		os.Exit(-1)
	}
	// 之后便是往该标准输入管道写入数据

	lineNum := 0 // 行数
	pageNum := 1 // 页数

	delim := '\n'
	if args.pageType == true {
		delim = '\f'
	}

	for {
		// 根据delim读取输入
		str, err := reader.ReadBytes(byte(delim))
		if err != nil {
			if err == io.EOF {
				// 将这一次的读入也写出
				if _, err := stdinPipe.Write(str); err != nil {
					os.Stderr.Write([]byte("ERROR: write bytes failed!\n"))
					os.Exit(-1)
				}
				break
			} else {
				os.Stderr.Write([]byte("ERROR: readbytes from file failed!\n"))
				os.Exit(-1)
			}
		}

		// 输出到标准输出或者管道
		if pageNum >= args.startPage && pageNum <= args.endPage {
			if _, err := stdinPipe.Write(str); err != nil {
				os.Stderr.Write([]byte("ERROR: write bytes failed!\n"))
				os.Exit(-1)
			}
		}

		if args.pageType == true {
			// 如果是\f定界，此时已经是一页
			pageNum++
		} else {
			// 否则此时只是增加行
			lineNum++
			// 判断是否足够一页
			if lineNum == args.pageLen {
				lineNum = 0 // 恢复计数
				pageNum++
			}
		}

		if pageNum > args.endPage {
			break
		}
	}
	// 显示关闭管道
	stdinPipe.Close()
	// 阻塞等待
	if err := cmd.Wait(); err != nil {
		os.Stderr.Write([]byte(err.Error()))
		os.Exit(-1)
	}
	// 检查参数
	if pageNum < args.startPage {
		os.Stderr.Write([]byte("ERROR: start page# is bigger than total page#\n"))
		os.Exit(-1)
	}

	if pageNum < args.endPage {
		os.Stderr.Write([]byte("ERROR: end page# is bigger than total page#\n"))
		os.Exit(-1)
	}
}
